<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      /**
       * javascript代码执行一段可执行代码时，会创建对应的执行上下文
       *
       * 对于每个执行上下文，都有三个重要属性：
       *  变量对象（Variable object，VO）
       *  作用域链（Scope chain）
       *  this
       *
       * 作用域链：当查找变量的时候，会先从当前上下文的变量对象中查找，如果没有找到，就会从父级（词法层面上的父级）执行上下文的变量对象中查找，
       * 一直找到全局上下文的变量对象，也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链
       *
       * 函数创建：函数作用域在函数定义的时候就决定了。这是因为函数有一个内部属性[[scope]]，当函数创建的时候，就会保存所有父变量对象到其中，
       * 你可以理解[[scope]]就是所有父变量对象的层级链，但是注意：[[scope]]并不代表完整的作用域链
       */
      function foo() {
        function bar() {}
      }

      // 函数创建时，各自的[[scope]]为：
      // foo.[[scope]] = [
      //   globalContext.VO
      // ]
      // bar.[[scope]] = [
      //   fooContext.AO,
      //   globalContext.AO
      // ]

      /**
       * 函数激活时：进入函数上下文，创建VO/AO后，就会将活动对象添加到作用链的前端（数组形式为例就是放在数组第一位）
       */
      // Scope = [AO].concat([[Scope]])

      // 例子
      var scope = 'global scope'
      function checkscope() {
        var scope2 = 'local scope'
        return scope2
      }
      // 1. checkscope 函数被创建，保存作用域链到内部属性[[scope]]
      // checkscope.[[scope]] = {
      //   globalCentext: VO
      // }

      // 2. 执行 checkscope 函数，创建checkscope函数执行上下文，checkscope函数执行上下文被压入执行上下文栈
      // ECSTACK = [checkscopeContext, globalContext]

      // 3. checkscope 函数并不立即执行，开始做准备工作，第一步：复制函数[[scope]]属性创建作用域链
      // checkscopeContext = {
      //   Scope: checkscope.[[scope]]
      // }

      // 4. 第二步，用arguments创建活动对象，随后初始化活动对象，加入形参、函数声明、变量声明
      // checkscopeContext = {
      //   AO: {
      //     arguments: {
      //       length: 0
      //     },
      //     scope2: undefined
      //   },
      //   Scope: checkscope.[[scope]]
      // }

      // 5. 第三步，将活动对象压入checkscope作用域链顶端
      // checkscopeContext = {
      //   AO: {
      //     arguments: {
      //       length: 0
      //     },
      //     scope2: undefined
      //   },
      //   Scope: [AO, [[Scope]]]
      // }

      // 6. 准备工作完成，开始执行函数，随着函数的执行，修改AO的属性值
      // checkscopeContext = {
      //   AO: {
      //     arguments: {
      //       length: 0
      //     },
      //     scope2: 'local scope'
      //   },
      //   Scope: [AO, [[Scope]]]
      // }

      // 7. 查找到scope2的值，返回后函数执行完毕，函数上下文从执行上下文栈中弹出
      // ECStack = [
      //   globalContext
      // ]


      var a = 1
      function a() {}
      console.log(a) // 输出 1
      /**
       * 代码解构为
       * var a
       * a = 1
       * function a() {}
       * console.log(a)
       * 
       * 在执行上下文的时候
       * VO: {
       *    a: reference to function a() {}
       * }
       * 
       * 在执行阶段，执行 a=1 的操作，修改变量对象的foo属性值，如果没有 a=1 的操作，那么输出的是函数
       * AO: {
       *    a: 1
       * }
       */
    </script>
  </body>
</html>
